package com.lordjoe.collectiveintelligence.svm.matchmaker;

import com.lordjoe.collectiveintelligence.data.ui.*;
import com.lordjoe.collectiveintelligence.svm.*;

import java.util.*;
import java.io.*;

import libsvm.*;

/**
 * com.lordjoe.collectiveintelligence.svm.MatchMaker
 *
 * @author Steve Lewis
 * @date May 20, 2009
 */
public class MatchMaker
{
    public static MatchMaker[] EMPTY_ARRAY = {};
    public static Class THIS_CLASS = MatchMaker.class;
    public static final String DATA_PATH = "data/svm/";

    public static final String HEADER_VERSION1 =
            "Age M\tSmoker M\tWant Children M\tInterests M\tAddress M\t" +
                    "Age F\tSmoker F\tWants Children F\tInterests F\tAddress F\tmatch";

    public static final String HEADER_VERSION2 =
            "Compatable\tAge M\tSmoker M\tWant Children M\tInterests M\tAddress M\t" +
                    "Age F\tSmoker FWants Children F\tInterests F\tAddress F\t" +
                    "LatLon M\tLatLon F\tDistance";

    public static MatchMaker buildMatchmaker(String textFile)
    {
        MatchMaker ret = new MatchMaker();
        String[] lines = SVMUtilities.readinLines(textFile);
        if (lines[0].equals(HEADER_VERSION1)) {
            // line 0 is the headers
            for (int i = 1; i < lines.length; i++) {
                String line = lines[i];
                String[] items = line.split("\t");
                PotentialPair pair = PotentialPair.buildPotentialPair(items);
                ret.addPotentialPair(pair);
            }
        }
        if (lines[0].equals(HEADER_VERSION2)) {
            // line 0 is the headers
            for (int i = 1; i < lines.length; i++) {
                String line = lines[i];
                String[] items = line.split("\t");
                PotentialPair pair = PotentialPair.buildPotentialPairVersion2(items);
                ret.addPotentialPair(pair);
            }
        }

        return ret;
    }

    private final List<PotentialPair> m_Pairs = new ArrayList<PotentialPair>();

    public MatchMaker()
    {
    }

    public void addPotentialPair(PotentialPair added)
    {
        m_Pairs.add(added);
    }

    public PotentialPair[] getPotentialPairs()
    {
        return m_Pairs.toArray(PotentialPair.EMPTY_ARRAY);
    }

    public double getMaximumDistance()
    {
        PotentialPair[] dates = getPotentialPairs();
        double ret = 500;
        for (int i = 0; i < dates.length; i++) {
            for (int j = 0; j < dates.length; j++) {
                PotentialPair date = dates[j];
                ret = Math.max(date.getDistance(), ret);
            }
        }
        return ret;
    }


    public PotentialDate[] getPotentialDates()
    {
        List<PotentialDate> holder = new ArrayList<PotentialDate>();
        PotentialPair[] pairs = getPotentialPairs();
        for (int i = 0; i < pairs.length; i++) {
            PotentialPair pair = pairs[i];
            holder.add(pair.getMale());
            holder.add(pair.getFemale());
        }
        PotentialDate[] ret = new PotentialDate[holder.size()];
        holder.toArray(ret);
        return ret;
    }

    public double getMinimumAge()
    {
        PotentialDate[] dates = getPotentialDates();
        double ret = 500;
        for (int i = 0; i < dates.length; i++) {
            for (int j = 0; j < dates.length; j++) {
                PotentialDate date = dates[j];
                ret = Math.min(date.getAge(), ret);
            }
        }
        return ret;
    }

    public double getMaximumAge()
    {
        PotentialDate[] dates = getPotentialDates();
        double ret = -500;
        for (int i = 0; i < dates.length; i++) {
            for (int j = 0; j < dates.length; j++) {
                PotentialDate date = dates[j];
                ret = Math.max(date.getAge(), ret);
            }
        }
        return ret;
    }

    public double getMinimumLatitude()
    {
        PotentialDate[] dates = getPotentialDates();
        double ret = 500;
        for (int i = 0; i < dates.length; i++) {
            for (int j = 0; j < dates.length; j++) {
                PotentialDate date = dates[j];
                ret = Math.min(date.getLatLon().getLat(), ret);
            }
        }
        return ret;
    }

    public double getMinimumLongitude()
    {
        PotentialDate[] dates = getPotentialDates();
        double ret = 500;
        for (int i = 0; i < dates.length; i++) {
            for (int j = 0; j < dates.length; j++) {
                PotentialDate date = dates[j];
                ret = Math.min(date.getLatLon().getLon(), ret);
            }
        }
        return ret;
    }

    public double getMaximumLatitude()
    {
        PotentialDate[] dates = getPotentialDates();
        double ret = -500;
        for (int i = 0; i < dates.length; i++) {
            for (int j = 0; j < dates.length; j++) {
                PotentialDate date = dates[j];
                ret = Math.max(date.getLatLon().getLat(), ret);
            }
        }
        return ret;
    }

    public double getMaximumLongitude()
    {
        PotentialDate[] dates = getPotentialDates();
        double ret = -500;
        for (int i = 0; i < dates.length; i++) {
            for (int j = 0; j < dates.length; j++) {
                PotentialDate date = dates[j];
                ret = Math.max(date.getLatLon().getLon(), ret);
            }
        }
        return ret;
    }


    private static void rewriteMatchMaker(PotentialPair[] pPairs)
            throws IOException
    {
        PrintWriter out = new PrintWriter(new FileWriter(DATA_PATH + "MatchMaker.tab2"));

        out.println(HEADER_VERSION2);
        for (int i = 0; i < pPairs.length; i++) {
            PotentialPair pair = pPairs[i];
            String line = pair.getTabbedLine();
            out.println(line);
            System.out.println(line);
        }
        out.close();
    }

    public static void showAgeEffect(PotentialPair[] pPairs)
    {
        List<DPoint> compatHolder = new ArrayList<DPoint>();
        List<DPoint> incompatHolder = new ArrayList<DPoint>();
        for (int i = 0; i < pPairs.length; i++) {
            PotentialPair pair = pPairs[i];
            DPoint agPair = new DPoint(pair.getMaleAge(), pair.getFemaleAge());
            if (pair.isCompatable())
                compatHolder.add(agPair);
            else
                incompatHolder.add(agPair);

        }
        DPoint[] compat = new DPoint[compatHolder.size()];
        compatHolder.toArray(compat);
        DPoint[] incompat = new DPoint[incompatHolder.size()];
        incompatHolder.toArray(incompat);
        PointPlot.showPlot("Age Effect", "Male Age", "Female Age", incompat, compat);
    }

    /**
     * plot good and bad predictions as a function of age
     *
     * @param pPairs
     */
    public static void showAgePredictionEffect(PotentialPair[] pPairs)
    {
        List<DPoint> compatGoodHolder = new ArrayList<DPoint>();
        List<DPoint> incompatGoodHolder = new ArrayList<DPoint>();
        List<DPoint> compatBadHolder = new ArrayList<DPoint>();
        List<DPoint> incompatBadHolder = new ArrayList<DPoint>();
        for (int i = 0; i < pPairs.length; i++) {
            PotentialPair pair = pPairs[i];
            DPoint agPair = new DPoint(pair.getMaleAge(), pair.getFemaleAge());
            if (pair.isCompatable()) {
                if (pair.isPredictionCorrect()) {
                    compatGoodHolder.add(agPair);
                }
                else {
                    compatBadHolder.add(agPair);
                }
            }
            else {
                if (pair.isPredictionCorrect()) {
                    incompatGoodHolder.add(agPair);
                }
                else {
                    incompatBadHolder.add(agPair);
                }
            }

        }
        DPoint[] compatGood = new DPoint[compatGoodHolder.size()];
        compatGoodHolder.toArray(compatGood);
        DPoint[] compatBad = new DPoint[compatBadHolder.size()];
        compatBadHolder.toArray(compatBad);

        DPoint[] incompatGood = new DPoint[incompatGoodHolder.size()];
        incompatGoodHolder.toArray(incompatGood);
        DPoint[] incompatBad = new DPoint[incompatBadHolder.size()];
        incompatBadHolder.toArray(incompatBad);

        PointPlot.showPlot("Age Effect Predict", "Male Age", "Female Age", incompatGood, compatGood,
                incompatBad, compatBad);
    }

    /**
     * plot good and bad predictions as a function of age
     *
     * @param pPairs
     */
    public static void showDistanceAndInterestsPredictionEffect(PotentialPair[] pPairs)
    {
        List<DPoint> compatGoodHolder = new ArrayList<DPoint>();
        List<DPoint> incompatGoodHolder = new ArrayList<DPoint>();
        List<DPoint> compatBadHolder = new ArrayList<DPoint>();
        List<DPoint> incompatBadHolder = new ArrayList<DPoint>();
        for (int i = 0; i < pPairs.length; i++) {
            PotentialPair pair = pPairs[i];
            double y = pair.getCommonInterests().size() +
                    0.1 * SVMUtilities.RND.nextGaussian();
            DPoint agPair = new DPoint(pair.getDistance(),
                    y);
            if (pair.isCompatable()) {
                if (pair.isPredictionCorrect())
                    compatGoodHolder.add(agPair);
                else
                    compatBadHolder.add(agPair);
            }
            else if (pair.isPredictionCorrect())
                incompatGoodHolder.add(agPair);
            else
                incompatBadHolder.add(agPair);

        }
        DPoint[] compatGood = new DPoint[compatGoodHolder.size()];
        compatGoodHolder.toArray(compatGood);
        DPoint[] compatBad = new DPoint[compatBadHolder.size()];
        compatBadHolder.toArray(compatBad);

        DPoint[] incompatGood = new DPoint[incompatGoodHolder.size()];
        incompatGoodHolder.toArray(incompatGood);
        DPoint[] incompatBad = new DPoint[incompatBadHolder.size()];
        incompatBadHolder.toArray(incompatBad);

        PointPlot.showPlot("Distance ls CommonInterests Predict", "Distance", "Common Interests", incompatGood, compatGood,
                incompatBad, compatBad);
    }


    public static void showDistanceAndInterests(PotentialPair[] pPairs)
    {
        List<DPoint> compatHolder = new ArrayList<DPoint>();
        List<DPoint> incompatHolder = new ArrayList<DPoint>();
        for (int i = 0; i < pPairs.length; i++) {
            PotentialPair pair = pPairs[i];
            double y = pair.getCommonInterests().size() +
                    0.1 * SVMUtilities.RND.nextGaussian();
            DPoint agPair = new DPoint(pair.getDistance(),
                    y);
            if (pair.isCompatable())
                compatHolder.add(agPair);
            else
                incompatHolder.add(agPair);

        }
        DPoint[] compat = new DPoint[compatHolder.size()];
        compatHolder.toArray(compat);
        DPoint[] incompat = new DPoint[incompatHolder.size()];
        incompatHolder.toArray(incompat);
        PointPlot.showPlot("Distance vs Common Interests", "Distance Km", "Common Interests",
                incompat, compat);
    }


    public static void showSmoking(PotentialPair[] pPairs)
    {
        List<DPoint> compatHolder = new ArrayList<DPoint>();
        List<DPoint> incompatHolder = new ArrayList<DPoint>();
        for (int i = 0; i < pPairs.length; i++) {
            PotentialPair pair = pPairs[i];
            DPoint agPair = new DPoint(SVMUtilities.booleanToFuzzy01(pair.isMaleSmoker()),
                    SVMUtilities.booleanToFuzzy01(pair.isFemaleSmoker()));
            if (pair.isCompatable())
                compatHolder.add(agPair);
            else
                incompatHolder.add(agPair);

        }
        DPoint[] compat = new DPoint[compatHolder.size()];
        compatHolder.toArray(compat);
        DPoint[] incompat = new DPoint[incompatHolder.size()];
        incompatHolder.toArray(incompat);
        PointPlot.showPlot("Smoker", "Male", "Female", incompat, compat);
    }

    protected static void showChildren(PotentialPair[] pPairs)
    {
        List<DPoint> compatHolder = new ArrayList<DPoint>();
        List<DPoint> incompatHolder = new ArrayList<DPoint>();
        for (int i = 0; i < pPairs.length; i++) {
            PotentialPair pair = pPairs[i];
            DPoint agPair = new DPoint(SVMUtilities.booleanToFuzzy01(pair.isMaleChildrenWanted()),
                    SVMUtilities.booleanToFuzzy01(pair.isFemaleChildrenWanted()));
            if (pair.isCompatable())
                compatHolder.add(agPair);
            else
                incompatHolder.add(agPair);

        }
        DPoint[] compat = new DPoint[compatHolder.size()];
        compatHolder.toArray(compat);
        DPoint[] incompat = new DPoint[incompatHolder.size()];
        incompatHolder.toArray(incompat);
        PointPlot.showPlot("Smoker", "Male", "Female", incompat, compat);
    }

    public static void main(String[] args) throws IOException
    {
        MatchMaker mm = buildMatchmaker(DATA_PATH + "MatchMaker.tab");
        PotentialPair[] pairs = mm.getPotentialPairs();

        showAgeEffect(pairs);
        showDistanceAndInterests(pairs);
        showSmoking(pairs);
        showChildren(pairs);

    }

}
